/*! \file
 * \brief Enter a brief description here.
 *
 * Enter a detailed description here. (optional)
 * \author Author (Ivo Wingelaar)
 * \date Date (2011)
 * \version Version (0.0.1)
 * \copyright Copyright (GNU LGPL v3)
 */

#include "LynxWindowManagerStub_C.hpp"
#include "LynxLogManager_C.hpp"
#include "LynxAudioManager_C.hpp"

namespace Lynx
{

/** constructor **/
/// \param    const WindowParameters& parameters
///         The parameters you want to use for this window.
WindowManagerStub_C::WindowManagerStub_C(const WindowParameters& parameters) :
    mParameters(parameters),
    mVideoManager(0),
    mAudioManager(0),
    mLogManager(0),
    mTimeManager(0),
    mCursorManager(0),
    mOSManager(0),
    mReceiver(parameters.mEventReceiver),
    mClose(false)
{
    mLogManager = new LogManager_C(mParameters.mLogLevel);
    if(mParameters.mConsoleLog)
    {
        LogOutput* output = new LogOutputConsole_C();
        mLogManager->add_output(output);
        output->decrement_counter();
    }

    mAudioManager = new Audio::AudioManager_C(mLogManager);
}

/** default destructor **/
WindowManagerStub_C::~WindowManagerStub_C()
{
    for(uint32_t a = 0; a < mEventObjects.size(); a++)
        mEventObjects[a]->decrement_counter();

    mEventObjects.clear();

    if(mTimeManager)
        mTimeManager->decrement_counter();
    if(mCursorManager)
        mCursorManager->decrement_counter();
    if(mOSManager)
        mOSManager->decrement_counter();
    if(mVideoManager)
        mVideoManager->decrement_counter();
    if(mAudioManager)
        mAudioManager->decrement_counter();
    if(mLogManager)
        mLogManager->decrement_counter();
}

/** virtual function      **/
/** set_double_click_time **/
//! Sets the double click time.
/// \return void
/// \param    uint32_t time_ms
///         The double click time in milliseconds.
void WindowManagerStub_C::set_double_click_time(uint32_t time_ms)
{
    mMouseMultiClicks.mDoubleClickTime = time_ms;
}

/** const virtual function **/
/** get_double_click_time  **/
//! Returns the double click time.
/// \return uint32_t
///         The double click time.
/// \param  void
uint32_t WindowManagerStub_C::get_double_click_time(void) const
{
    return mMouseMultiClicks.mDoubleClickTime;
}

/** const virtual function **/
/** is_window_fullscreen   **/
//! Returns if the window is fullscreen.
/// \return bool
///         True if the window is fullscreen, false otherwise.
/// \param  void
bool WindowManagerStub_C::is_window_fullscreen(void) const
{
    return mParameters.mFullscreen;
}

/** virtual function     **/
/** post_event_from_user **/
//! Posts an event from the user.
/// \return bool
///         True if the event was absorbed, false otherwise.
/// \param    const Event &event
///         The event you want to post.
bool WindowManagerStub_C::post_event_from_user(const Event& event)
{
    bool absorbed = false;

    if(mReceiver)
        absorbed = mReceiver->on_event(event);

    if(absorbed)
        return true;

    for(uint32_t a = 0; a < mEventObjects.size(); a++)
    {
        absorbed = mEventObjects[a]->post_event_from_user(event);
        if(absorbed)
            return true;
    }

    return absorbed;
}

/** const virtual function **/
/** get_event_receiver     **/
//! Returns the eventreceiver.
/// \return EventReceiver*
///         The eventreceiver.
/// \param  void
EventReceiver* WindowManagerStub_C::get_event_receiver(void) const
{
    return mReceiver;
}

/** virtual function   **/
/** set_event_receiver **/
//! Sets a new eventreceiver.
/// \return void
/// \param    EventReceiver *receiver
///         A pointer to the new eventreceiver.
void WindowManagerStub_C::set_event_receiver(EventReceiver* receiver)
{
    mReceiver = receiver;
    if(mLogManager)
        ((LogManager_C*)mLogManager)->set_event_receiver(mReceiver);

    for(uint32_t a = 0; a < mEventObjects.size(); a++)
        mEventObjects[a]->set_user_event_receiver(mReceiver);
}

/** virtual function           **/
/** add_event_receiving_object **/
//! Adds a event receiving object to the eventlist.
/// \return void
/// \param    EventReceivingObject* object
///         A pointer to the event receiving object you want to add.
void WindowManagerStub_C::add_event_receiving_object(EventReceivingObject* object)
{
    mEventObjects.push_back(object);
    object->set_user_event_receiver(mReceiver);
    object->increment_counter();
}

/** const virtual function           **/
/** get_event_receiving_object_count **/
//! Returns amount of event receiving object that are loaded.
/// \return uint32_t
///         The total amount of event receiving objects.
/// \param  void
uint32_t WindowManagerStub_C::get_event_receiving_object_count(void) const
{
    return mEventObjects.size();
}

/** virtual function           **/
/** get_event_receiving_object **/
//! Returns a event receiving object.
/// \return EventReceivingObject*
/// \param    uint32_t index
///         A pointer to the event receiving object you want to add.
EventReceivingObject* WindowManagerStub_C::get_event_receiving_object(uint32_t index) const
{
    return mEventObjects[index];
}

/** const virtual function **/
/** get_video_manager      **/
//! Returns the pointer to the videomanager.
/// \return Video::VideoManager*
///         The pointer to the videomanager.
/// \param  void

VideoManager* WindowManagerStub_C::get_video_manager(void) const
{
    return mVideoManager;
}

Audio::AudioManager* WindowManagerStub_C::get_audio_manager(void) const
{
    return mAudioManager;
}

/** const virtual function **/
/** get_log_manager        **/
//! Returns the pointer to the logmanager.
/// \return LogManager*
///         The pointer to the logmanager.
/// \param  void
LogManager* WindowManagerStub_C::get_log_manager(void) const
{
    return mLogManager;
}

/** const virtual function **/
/** get_time_manager       **/
//! Returns the pointer to the timemanager.
/// \return TimeManager*
///         The pointer to the timemanager.
/// \param  void
TimeManager* WindowManagerStub_C::get_time_manager(void) const
{
    return mTimeManager;
}

/** const virtual function **/
/** get_cursor_manager     **/
//! Returns the pointer to the cursormanager.
/// \return CursorManager*
///         The pointer to the cursormanager.
/// \param  void
CursorManager* WindowManagerStub_C::get_cursor_manager(void) const
{
    return mCursorManager;
}

/** const virtual function **/
/** get_os_manager         **/
//! Returns the pointer to the osmanager.
/// \return OSManager*
///         The pointer to the osmanager.
/// \param  void
OSManager* WindowManagerStub_C::get_os_manager(void) const
{
    return mOSManager;
}

/** virtual function        **/
/** check_successive_clicks **/
//! Returns how many clicks where registered on a specific point.
/// \return uint32_t
///         The number of clicks.
/// \param    int32_t mouse_x
///         The x-coord of the mouse event.
/// \param    int32_t mouse_y
///         The y-coord of the mouse event.
/// \param    LYNX_E_MOUSE_EVENT input_event
///         The type of the event.
uint32_t WindowManagerStub_C::check_successive_clicks(int32_t mouse_x,
        int32_t mouse_y,
        LYNX_E_MOUSE_EVENT input_event)
{
    const int32_t MAX_MOUSEMOVE = 3;

    uint32_t clickTime = get_time_manager()->get_real_time();

    if((clickTime-mMouseMultiClicks.mLastClickTime) < mMouseMultiClicks.mDoubleClickTime
            && Myth::Math::abs(mMouseMultiClicks.mLastClick.X - mouse_x) <= MAX_MOUSEMOVE
            && Myth::Math::abs(mMouseMultiClicks.mLastClick.Y - mouse_y) <= MAX_MOUSEMOVE
            && mMouseMultiClicks.mCountSuccessiveClicks < 3
            && mMouseMultiClicks.mLastMouseInputEvent == input_event
      )
        ++mMouseMultiClicks.mCountSuccessiveClicks;
    else
        mMouseMultiClicks.mCountSuccessiveClicks = 1;

    mMouseMultiClicks.mLastMouseInputEvent = input_event;
    mMouseMultiClicks.mLastClickTime = clickTime;
    mMouseMultiClicks.mLastClick.X = mouse_x;
    mMouseMultiClicks.mLastClick.Y = mouse_y;

    return mMouseMultiClicks.mCountSuccessiveClicks;
}

/** function             **/
/** calculate_gamma_ramp **/
//! Helper function for calculation of the gamma ramp.
/// \return void
/// \param    uint16_t* ramp
///         The calculated ramp.
/// \param    float gamma
///         The gamma value.
/// \param    float relative_brightness
///         The relative brightness.
/// \param    float relative_contrast
///         The relative contrast.
void WindowManagerStub_C::calculate_gamma_ramp(uint16_t* ramp,
        float gamma,
        float relative_brightness,
        float relative_contrast)
{
    int32_t i;
    int32_t value;
    int32_t rbright = (int32_t)(relative_brightness * (65535.f / 4));
    float rcontrast = 1.f / (255.f - (relative_contrast * 127.5f));

    gamma = gamma > 0.f ? 1.0f / gamma : 0.f;

    for(i = 0; i < 256; ++i)
    {
        value = (int32_t)(pow(rcontrast * i, gamma)*65535.f + 0.5f);
        ramp[i] = (uint16_t)Myth::Math::clamp(value + rbright, 0, 65535);
    }
}

/** function                  **/
/** calculate_gamma_from_ramp **/
//! Calculates the gamma value from a ramp.
/// \return void
/// \param    float& gamma
///         The gamma value.
/// \param    const uint16_t* ramp
///         The gamma ramp.
void WindowManagerStub_C::calculate_gamma_from_ramp(float& gamma,
        const uint16_t* ramp)
{
    float sum = 0.0;
    int32_t i, count = 0;

    gamma = 1.0;
    for(i = 1; i < 256; ++i)
    {
        if((ramp[i] != 0) && (ramp[i] != 65535))
        {
            float B = (float)i / 256.f;
            float A = ramp[i] / 65535.f;
            sum += (float)(logf(A) / logf(B));
            count++;
        }
    }
    if(count && sum)
        gamma = 1.0f / (sum / count);
}

} // namespace Lynx

